
// Rich Calendar
RichCalendar = function(target_obj, show_time) {

	// value
	this.value = '';

	// format
	this.format = '%Y-%m-%d';

	// Week Day to start with (0 - Sunday, 1 - Monday, etc...)
	this.start_week_day = 6;

	// iframe object to show calendar object in
	this.iframe_obj = null;

	// path to calendar css and js files
	this.lib_path = 'rich_calendar/';

	// DOM object to take/set date from/to
	this.target_obj = target_obj;

	// show time
	this.show_time = show_time;

	// function called when calendar value changes
	this.user_onchange_handler = null;

	// function called when data choice is cancelled
	this.user_onclose_handler = null;

	// function called when mouse clicked outside calendar with auto_close set
	// to true after it is closed
	this.user_onautoclose_handler = null;

	// default language
	this.default_language = 'en';

	// language
	this.language = 'ar';

	// current date
	this.date = new Date();
/*
this.date.setFullYear(2008);
this.date.setMonth(1);
this.date.setDate(29);
*/
//this.date.setMonth(11);
//this.date.setDate(31);

	// calendar skin name
	this.skin = '';

	// calendar closes automatically on click outside it
	this.auto_close = true;

	// element which value is taken to initilize calendar and where calendar
	// returns date if user defined function to return date is not specified
	this.value_el = null;

	// specifies calendar positioning - absolute by default
	this.position = null;

}

RichCalendar.is_ie = /msie/i.test(navigator.userAgent) && !/opera/i.test(navigator.userAgent);

// Static functions
RichCalendar.get_iframe_styles = function() {
var i;
var j;

	var styles = document.styleSheets;
	var sheets_num = styles.length;

	var style_text = '';
    for (i=0; i<sheets_num; i++) {
		if (RichCalendar.is_ie) {
			// take all calendar styles in IE as cannot take text of each rule
			if (/rich_calendar.css$/.test(styles[i].href)) {
				style_text += styles[i].cssText;
				break;
			}
		} else {
			var rules = null;
			try {
				if (RichCalendar.is_ie) {
					rules = styles[i].rules;
				} else {
					rules = styles[i].cssRules;
				}
			} catch(error) {
				continue;
			}

			if (rules != null) {
				rules_num = rules.length;

				for (j=0; j<rules_num; j++) {
					var rule_value = rules[j].selectorText;
					if (/rc_iframe/.test(rule_value)) {
						style_text += rules[j].cssText;
					}
				}
			}
		}
	}

	return style_text;
}


RichCalendar.attach_event = function(obj, event, handler) {

	if (obj.addEventListener) {
		obj.addEventListener(event, handler, false);
	} else {
		if (obj.attachEvent) {
			obj.attachEvent('on'+event, handler);
		}
	}
}


RichCalendar.detach_event = function(obj, event, handler) {

	if (obj.removeEventListener) {
		obj.removeEventListener(event, handler, false);
	} else {
		if (obj.detachEvent) {
			obj.detachEvent('on'+event, handler);
		}
	}
}


// add event handlers to object obj
RichCalendar.attach_events = function(obj) {
	RichCalendar.attach_event(obj, 'click', RichCalendar.onclick);
	RichCalendar.attach_event(obj, 'mouseover', RichCalendar.onmouseover);
	RichCalendar.attach_event(obj, 'mouseout', RichCalendar.onmouseout);
}


// remove event handlers set to object obj
RichCalendar.detach_events = function(obj) {
	RichCalendar.detach_event(obj, 'click', RichCalendar.onclick);
	RichCalendar.detach_event(obj, 'mouseover', RichCalendar.onmouseover);
	RichCalendar.detach_event(obj, 'mouseout', RichCalendar.onmouseout);
}


// calendar onclick event handler
RichCalendar.onclick = function(e) {

//alert(e + ' => ' + e.srcElement + ' => ' + e.target + ' => ' + window.event);
//for (var i in e) alert(i + ' => ' + e[i]);

var event = RichCalendar.get_event(e);
var obj = RichCalendar.get_target_object(e);

	if (!obj) return;

var cal = obj.calendar;

var cur_year = cal.date.getFullYear();
var cur_month = cal.date.getMonth();
var cur_day = cal.date.getDate();

//alert(obj.rc_object_code);
	switch (obj.rc_object_code) {
		case 'day':
//			alert(obj.day_num);
			cal.date.setDate(obj.day_num);
			break;
		case 'prev_year':
			// determine number of days in prev year
			cal.date.setDate(1);
			cal.date.setFullYear(cur_year-1);
			var month_days = RichCalendar.get_month_days(cal.date);

			// prevent jumping to next month
			if (cur_day > month_days) {
				cal.date.setDate(month_days);
			} else {
				cal.date.setDate(cur_day);
			}

			cal.show_date();
			break;
		case 'prev_month':
			// determine number of days in prev month
			cal.date.setDate(1);
			cal.date.setMonth(cur_month-1);
			var month_days = RichCalendar.get_month_days(cal.date);

			// prevent jumping to next month
			if (cur_day > month_days) {
				cal.date.setDate(month_days);
			} else {
				cal.date.setDate(cur_day);
			}

			cal.show_date();
			break;
		case 'next_month':
			// determine number of days in prev month
			cal.date.setDate(1);
			cal.date.setMonth(cur_month+1);
			var month_days = RichCalendar.get_month_days(cal.date);

			// prevent jumping to next month
			if (cur_day > month_days) {
				cal.date.setDate(month_days);
			} else {
				cal.date.setDate(cur_day);
			}

			cal.show_date();
			break;
		case 'next_year':
			// determine number of days in next year
			cal.date.setDate(1);
			cal.date.setFullYear(cur_year+1);
			var month_days = RichCalendar.get_month_days(cal.date);

			// prevent jumping to next month
			if (cur_day > month_days) {
				cal.date.setDate(month_days);
			} else {
				cal.date.setDate(cur_day);
			}

			cal.show_date();
			break;
		case 'today':
			var today = new Date();
			today.setHours(cal.date.getHours());
			today.setMinutes(cal.date.getMinutes());
			today.setSeconds(cal.date.getSeconds());

			cal.date = today;
			cal.show_date();

			break;
		case 'clear':
			// handle clear request
			if (cal.value_el) {
				cal.value_el.value = '';
			}
			break;
		case 'close':
			// handle close request
			cal.onclose_handler();
			break;
		case 'week_day':
//alert(obj.innerHTML);
			cal.start_week_day = obj.week_day_num;
			cal.show_date();
			break;
		default:
			break;
	}


	// handle close request
	if (obj.rc_object_code != 'week_day') {
		cal.onchange_handler(obj.rc_object_code);
	}

	// handle date change


	// hide all other auto closing calendars
	RichCalendar.hide_auto_close(cal);

}


// calendar onmouseover event handler
RichCalendar.onmouseover = function(e) {

//alert(e + ' => ' + e.srcElement + ' => ' + e.target + ' => ' + window.event);
//for (var i in e) alert(i + ' => ' + e[i]);

var event = RichCalendar.get_event(e);
var obj = RichCalendar.get_target_object(e);

	if (!obj) return;

var cal = obj.calendar;

var cur_year = cal.date.getFullYear();
var cur_month = cal.date.getMonth();
var cur_day = cal.date.getDate();

	switch (obj.rc_object_code) {
		case 'day':
			var date = new Date(cal.date);
			date.setDate(obj.day_num);
			cal.set_footer_text(cal.get_formatted_date(cal.text('footerDateFormat'), date));

			// highlight day cell and its row
			RichCalendar.add_class(obj, "rc_highlight");
			RichCalendar.add_class(obj.parentNode, "rc_highlight");

			break;
		case 'clear':
		case 'today':
		case 'close':
		case 'prev_year':
		case 'prev_month':
		case 'next_month':
		case 'next_year':
			cal.set_footer_text(cal.text(obj.rc_object_code));
			break;
		case 'week_day':
			if (obj.week_day_num != cal.start_week_day) {
				var day_names = cal.text("dayNames");
				var name = day_names[obj.week_day_num];
				var text = cal.text("make_first");
				text = text.replace("%s", name);
			} else {
				var text = cal.text('footerDefaultText');
			}
			cal.set_footer_text(text);
			break;
		default:
			cal.set_footer_text(cal.text('footerDefaultText'));
			break;
	}

}


// calendar onmouseout event handler
RichCalendar.onmouseout = function(e) {

//alert(e + ' => ' + e.srcElement + ' => ' + e.target + ' => ' + window.event);
//for (var i in e) alert(i + ' => ' + e[i]);

var event = RichCalendar.get_event(e);
var obj = RichCalendar.get_target_object(e);

	if (!obj) return;

var cal = obj.calendar;

	cal.set_footer_text(cal.text('footerDefaultText'));

	// un-highlight day cell and its row
	RichCalendar.remove_class(obj, "rc_highlight");
	RichCalendar.remove_class(obj.parentNode, "rc_highlight");

}


// document onmousedown event handler
RichCalendar.document_onmousedown = function(e) {
var event = RichCalendar.get_event(e);
var obj = RichCalendar.get_target_object(e);

	if (!obj) return;

var el = obj;
var cal = null;

	while (el) {
		if (el.className && el.className.match(/^rc_iframe_body/) &&
			el.tagName.toUpperCase() == 'BODY') {

			cal = el.calendar;
			break;
		}
		el = el.parentNode;
	}

	// close all not active calendars
	RichCalendar.hide_auto_close(cal);

}


// hide all calendars that should autoclose except cal and remove
// them from RichCalendar.active_calendars
RichCalendar.hide_auto_close = function(cal) {
var active_cals = [];
var i;

	for (i=0; i<RichCalendar.active_calendars.length; i++) {
		var cur_cal = RichCalendar.active_calendars[i];
		if (cur_cal.auto_close && cur_cal != cal) {

			cur_cal.hide();

			if (cur_cal.user_onautoclose_handler) {
				cur_cal.user_onautoclose_handler(this);
			}

		} else {
			active_cals[active_cals.length] = cur_cal;
		}
	}

	RichCalendar.active_calendars = active_cals;
}


// remove calendar cal from list RichCalendar.active_calendars of active
// calendars
RichCalendar.make_inactive = function(cal) {
var active_cals = [];
var i;

	for (i=0; i<RichCalendar.active_calendars.length; i++) {
		var cur_cal = RichCalendar.active_calendars[i];
		if (cur_cal != cal) {
			active_cals[active_cals.length] = cur_cal;
		}
	}

	RichCalendar.active_calendars = active_cals;
}


// returns event object
RichCalendar.get_event = function(e) {

	return e||window.event;

}


// returns event target object
RichCalendar.get_target_object = function(e) {

	return e.target?e.target:(e.srcElement?e.srcElement:window.event.srcElement);

}


// returns skin suffics for skin class name
RichCalendar.skin_suffix = function(skin) {
	return (skin != '')?('_' + skin):'';
}


// return number of days in month
RichCalendar.get_month_days = function(date, month) {
var year = date.getFullYear();

	if (month) {
		month = parseInt(month);
		if (month <= 0 || month > 11) month = null;
	}

	if (!month) {
		month = date.getMonth();
	}

	if (month==1 && RichCalendar.is_leap_year(year)) {
		return 29;
	} else {
//alert(month + ' -> ' + RichCalendar.month_days[month]);
		return RichCalendar.month_days[month];
	}

}


// return true if year is a leap year
RichCalendar.is_leap_year = function(year) {
	return (year%4==0 && year%100!=0 || year%400==0) ? true : false;
}


// return day of the year
RichCalendar.get_day_of_year = function(date) {
var now = new Date(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
var year_start = new Date(date.getFullYear(), 0, 0, 0, 0, 0);

// milliseconds in day
var day_in_msecs = 24*60*60*1000;

	return Math.floor((now - year_start) / day_in_msecs);

}


// add class to element
RichCalendar.add_class = function(el, class_name) {

	RichCalendar.remove_class(el, class_name);
	el.className += " " + class_name;

}


// remove class from element
RichCalendar.remove_class = function(el, class_name) {

	if (!el || !el.className) return

	var new_class_parts = [];
	var class_parts = String(el.className).split(" ");
	var i;
	for (i=0; i<class_parts.length; i++) {
		if (class_parts[i] != "" && class_parts[i] != class_name) {
			new_class_parts[new_class_parts.length] = class_parts[i];
		}
	}

	el.className = new_class_parts.join(" ");
}


// return position of object obj; dont go above stop_obj in DOM structure
RichCalendar.get_obj_pos = function(obj, stop_obj){
	var pos = Array(0,0);

	if (!obj) return pos;

	var iniObj = obj;

	while (obj && stop_obj != obj) {

		pos[0] += obj.offsetLeft;
		pos[1] += obj.offsetTop;

		if (obj != iniObj) {
			pos[0] += parseInt(RichCalendar.get_style(obj, "borderTopWidth"), 10) || 0;
			pos[1] += parseInt(RichCalendar.get_style(obj, "borderLeftWidth"), 10) || 0;
		}

		obj = obj.offsetParent;

	}


	var obj = iniObj;

	while (obj && stop_obj != obj && obj.tagName.toLowerCase() != 'body') {
		pos[0] -= obj.scrollLeft;
		pos[1] -= obj.scrollTop;

		obj = obj.parentNode;
	}

	return pos;
}

// return current style value
RichCalendar.get_style = function (el, name) {
var view = document.defaultView;

	if (view && view.getComputedStyle) {
		var st = view.getComputedStyle(el, "");
		return st[name];
	}

//		if (document.defaultView.getComputedStyle(obj, '').getPropertyValue('position') == 'absolute') break;

	var v;
	if (v = el.currentStyle) {
		return v[name];
	}

	if (v = el.style[name]) {
		return v;
	}

}

// array of text data in various languages
RichCalendar.rc_lang_data = [];

// number of days in months
RichCalendar.month_days = [31,28,31,30,31,30,31,31,30,31,30,31];

// currently shown calendars
RichCalendar.active_calendars = [];

// true if all document handlers are set
RichCalendar.handlert_set = false;


// Calendar API


// show calendar inside/before/after (defined by argument position) element el
// if any specified or in the point specified (if any)
RichCalendar.prototype.show = function(x, y, el, position) {

	if (!this.value_el) {
		this.value_el = el;
	}

	this.position = position;

	this.iframe_obj = document.createElement('IFRAME');
	this.iframe_obj.className = 'rc_calendar'+RichCalendar.skin_suffix(this.skin);
	this.iframe_obj.setAttribute('scrolling', 'no');
	this.iframe_obj.setAttribute('src','javascript:false;');
	this.iframe_obj.calendar = this;


	// relative positioning
	if (this.is_relative_position(position)) {
		switch (position) {
			case "before":
				if (el.parentNode) {
					el.parentNode.insertBefore(this.iframe_obj, el);
				}
				break;
			case "after":
				if (el.parentNode) {
					el.parentNode.insertBefore(this.iframe_obj, el.nextSibling);
				}
				break;
			case "child":
			default:
				el.appendChild(this.iframe_obj);
				this.position = 'child';
				break;
		}

	} else { // absolute positioning

		this.iframe_obj.style.position = 'absolute';

		// move the iframe to the position specified
		var left = parseInt(x);
		var top = parseInt(y);
		if (typeof(x) == 'number' && typeof(y) == 'number') {
			this.iframe_obj.style.left = x + 'px';
			this.iframe_obj.style.top = y + 'px';
		}
		this.iframe_obj.style.border = '1px solid #ccc';
		this.iframe_obj.value = this.value;

		document.body.appendChild(this.iframe_obj);

	}

	// styles to add to iframe
	var iframe_styles = RichCalendar.get_iframe_styles();

	// put calendar content into the iframe
	var iframe_content = '' +
'<html>' +
'<head>' +
'<style type="text/css">'+iframe_styles+'</style>' +
'</head>' +
'<body class="rc_iframe_body' + RichCalendar.skin_suffix(this.skin) + '" id="rc_body">' +
'</body></html>' +
	'';


	this.iframe_doc = this.iframe_obj.contentWindow.document;
	this.iframe_doc.open();
	this.iframe_doc.write(iframe_content);
	this.iframe_doc.close();

	RichCalendar.attach_event(this.iframe_doc, 'mousedown', RichCalendar.document_onmousedown);



	this.body_obj = this.iframe_doc.getElementById('rc_body');
	this.body_obj.calendar = this;
	// main table
	this.table_obj = this.iframe_doc.createElement('TABLE');
	this.table_obj.className = 'rc_table';
	this.table_obj.setAttribute('id', 'rc_iframe_table');
	this.table_obj.cellSpacing = 0;
	this.table_obj.cellPadding = 0;
	// store reference to the calendar
	this.table_obj.calendar = this;

	// header row
	this.head_tr = this.table_obj.insertRow(0);
	this.head_tr.className = 'rc_head_tr';

	this.clear_td = this.head_tr.insertCell(0);
	this.clear_td.innerHTML = 'c';
	this.clear_td.rc_object_code = 'clear';
	this.clear_td.calendar = this;
	RichCalendar.attach_events(this.clear_td);

//this.clear_td.className = 'rc_head_tr';
	this.head_td = this.head_tr.insertCell(1);
//this.head_td.className = 'rc_head_tr';
	this.head_td.colSpan = 5;
//this.head_td.innerHTML = 'asdf';
	this.close_td = this.head_tr.insertCell(2);
	this.close_td.innerHTML = 'x';
	this.close_td.rc_object_code = 'close';
	this.close_td.calendar = this;
	RichCalendar.attach_events(this.close_td);
//this.close_td.className = 'rc_head_tr';

	// navigation row
	this.nav_tr = this.table_obj.insertRow(1);
	this.nav_tr.className = 'rc_nav_tr';

	this.prev_year_td = this.nav_tr.insertCell(0);
	this.prev_year_td.innerHTML = '&#x00ab;';
	this.prev_year_td.rc_object_code = 'prev_year';
	this.prev_year_td.calendar = this;
	RichCalendar.attach_events(this.prev_year_td);

	this.prev_month_td = this.nav_tr.insertCell(1);
	this.prev_month_td.innerHTML = '&#x2039;';
	this.prev_month_td.rc_object_code = 'prev_month';
	this.prev_month_td.calendar = this;
	RichCalendar.attach_events(this.prev_month_td);

	this.today_td = this.nav_tr.insertCell(2);
	this.today_td.colSpan = 3;
	this.today_td.innerHTML = this.text('today');
	this.today_td.rc_object_code = 'today';
	this.today_td.calendar = this;
	RichCalendar.attach_events(this.today_td);

	this.next_month_td = this.nav_tr.insertCell(3);
	this.next_month_td.innerHTML = '&#x203a;';
	this.next_month_td.rc_object_code = 'next_month';
	this.next_month_td.calendar = this;
	RichCalendar.attach_events(this.next_month_td);

	this.next_year_td = this.nav_tr.insertCell(4);
	this.next_year_td.innerHTML = '&#x00bb;';
	this.next_year_td.rc_object_code = 'next_year';
	this.next_year_td.calendar = this;
	RichCalendar.attach_events(this.next_year_td);

	// weekdays row
	this.wd_tr = this.table_obj.insertRow(2);
	this.wd_tr.className = 'rc_wd_tr';

	var i;
//	var day_names = this.text('dayNamesShort');
	for (i=0; i<7; i++) {
//		var wd = (i+this.start_week_day)%7;

		var td = this.wd_tr.insertCell(i);
		td.rc_object_code = 'week_day';
		td.calendar = this;
		RichCalendar.attach_events(td);
//		td.innerHTML = day_names[wd];

//		if (typeof(weekend_days[wd]) != "undefined") {
//			td.className = "rc_weekend_head";
//		}
	}


	// calendar rows (initially create min number of rows necessary - 4)
	var rows_num = 4;
	var row_indx;
	var cell_indx;
	this.cal_tr = [];

	for (row_indx=0; row_indx<rows_num; row_indx++) {
		this.create_cal_row(row_indx);
/*
		this.cal_tr[row_indx] = this.table_obj.insertRow(3+row_indx);
		this.cal_tr[row_indx].className = 'rc_cal_tr';

		for (cell_indx=0; cell_indx<7; cell_indx++) {
			var td = this.cal_tr[row_indx].insertCell(cell_indx);
			td.innerHTML = row_indx + '-' + cell_indx;
		}
*/
	}


	if (this.show_time) {
		// create time row if necessary
		this.time_tr = this.table_obj.insertRow(rows_num+3);
		this.time_tr.className = 'rc_time_tr';
		var td = this.time_tr.insertCell(0);
		td.colSpan = 2;
		td.innerHTML = this.text('time') + ':';
	
		var td = this.time_tr.insertCell(1);
		td.colSpan = 3;

		this.hours_obj = this.createElement('INPUT', td);
		this.hours_obj.className = 'rc_hours';
		this.hours_obj.setAttribute('size', 1);
		this.hours_obj.setAttribute('maxlength', 2);

		this.colon_span = this.createElement('SPAN', td);
		this.colon_span.className = 'rc_colon_span';
		this.colon_span.innerHTML = '&nbsp;:&nbsp;';

		this.mins_obj = this.createElement('INPUT', td);
		this.mins_obj.className = 'rc_mins';
		this.mins_obj.setAttribute('size', 1);
		this.mins_obj.setAttribute('maxlength', 2);
	
		var td = this.time_tr.insertCell(2);
		td.colSpan = 2;
		td.innerHTML = '&nbsp;';
	}

	// footer row

	this.footer_tr = this.table_obj.insertRow(rows_num+3+(this.show_time?1:0));
	this.footer_tr.className = 'rc_footer_tr';
	this.footer_td = this.footer_tr.insertCell(0);
	this.footer_td.colSpan = 7;
	this.footer_td.innerHTML = this.text('footerDefaultText');


	this.body_obj.appendChild(this.table_obj);

	// create a DIV element to determine size of calendar
	this.size_div = document.createElement('DIV');
	this.size_div.className = this.body_obj.className;
	this.size_div.style.position = "absolute";
	this.size_div.style.left = "-1000px";
	this.size_div.style.top = "-1000px";
	document.body.appendChild(this.size_div);


	// show current date in calendar
	this.show_date();


	// set document handlers if not set yet
	if (!RichCalendar.handlers_set) {
		RichCalendar.attach_event(document, 'mousedown', RichCalendar.document_onmousedown);
		RichCalendar.handlers_set = true;
	}

	// store this calendar in array of active calendars
	RichCalendar.active_calendars[RichCalendar.active_calendars.length] = this;

//alert(this.body_obj.innerHTML);
}


// hide calendar (destroy iframe object)
RichCalendar.prototype.hide = function() {
	if (this.iframe_obj) {
		this.iframe_obj.parentNode.removeChild(this.iframe_obj);
		this.iframe_obj = null;
	}

	RichCalendar.make_inactive(this);
}


// show calendar inside/before/after (defined by argument position) element el
// ie relative to element el or 
RichCalendar.prototype.show_at_element = function(el, position) {

	if (typeof(el) != "object" || !el) return;

	// relative positioning
	if (this.is_relative_position(position)) {
		this.show(null, null, el, position);
		return;
	}
/*
	switch (position) {
		case "before":
		case "after":
		case "child":
			this.show(null, null, el, position);
			return;
		default:
			break;
	}
*/

	// absolute positioning
	var el_pos = RichCalendar.get_obj_pos(el);
	// negative coordinates to make calendar invisible for a while
	// as cannot determine right coordinates right now (calendar size is not
	// known yet right after this.show worked)
	var x = -1000;
	var y = -1000;

	this.show(x, y, el, position);


	// fix position (need to do this later then calendar is shown as
	// size of calendar could change in this.show(x, y)
//	var cal = this;
//	window.setTimeout(function(){cal.fix_position(el, position)}, 5);

}

// fix position of calendar
RichCalendar.prototype.fix_position = function(el) {
var position = this.position;

	if (this.is_relative_position(position)) {
		return;
	}

	if (!el) {
		el = this.value_el;
	}

//	alert(el.getAttribute("id") + " => " + position);

	var aligns = String(position).split("-");
	if (aligns.length == 2) {

		var el_pos = RichCalendar.get_obj_pos(el);
//alert(el_pos + ' => ' + el.offsetHeight);
		var x = el_pos[0];
		var y = el_pos[1] + el.offsetHeight;

		// iframe border thikness
		var border_width = parseInt(this.iframe_obj.style.borderWidth);

		var cal_width = parseInt(this.iframe_obj.width) + 2*border_width;
		var cal_height = parseInt(this.iframe_obj.height) + 2*border_width;

//alert('!: ' + cal_width + ' => ' + cal_height);
		// horizontal alignment
		switch (aligns[0]) {
			case "left":
				x -= cal_width;
				break;
			case "center":
				x += (el.offsetWidth - cal_width) / 2;
				break;
			case "right":
				x += el.offsetWidth;
				break;
			case "adj_right":
				x += el.offsetWidth - cal_width;
				break;
			default:
				break;
		}

		// vertical alignment
		switch (aligns[1]) {
			case "top":
				y -= el.offsetHeight + cal_height;
				break;
			case "center":
				y += (el.offsetHeight - cal_height) / 2 - el.offsetHeight;
				break;
			case "bottom":
				break;
			case "adj_bottom":
				y -= cal_height;
				break;
			default:
				break;
		}

		this.iframe_obj.style.left = x + 'px';
		this.iframe_obj.style.top = y + 'px';

		this.iframe_obj.style.visibility = 'visible';
	}

}


// return true if calendar is relatively positioned
RichCalendar.prototype.is_relative_position = function(position) {
	switch (position) {
		case "before":
		case "after":
		case "child":
			return true;
		default:
			return false;
	}
}


// creates an element in iframe
RichCalendar.prototype.createElement = function(tagName, parent) {

var el = this.iframe_doc.createElement(tagName);

	if (parent) {
		parent.appendChild(el);
	}

	return el;
}


// return text data desired
RichCalendar.prototype.text = function(name, language) {

	if (typeof(language) == "undefined") {
		language = this.language;
	}

	if (typeof(RichCalendar.rc_lang_data[language]) != "undefined") {
		return typeof(RichCalendar.rc_lang_data[language][name]) != "undefined"?RichCalendar.rc_lang_data[language][name]:'';
	}

	return typeof(RichCalendar.rc_lang_data[this.default_language][name]) != "undefined"?RichCalendar.rc_lang_data[this.default_language][name]:'';

}

// show date in calendar
RichCalendar.prototype.show_date = function() {
	// update week days row

	// numbers of weekend days
	var weekend_days = this.get_weekend_days();

	var i;
	var day_names = this.text('dayNamesShort');
	for (i=0; i<7; i++) {
		var wd = (i+this.start_week_day)%7;

		var td = this.wd_tr.cells[i];
		td.innerHTML = day_names[wd];

		if (typeof(weekend_days[wd]) != "undefined") {
			td.className = "rc_weekend_head";
		} else {
			td.className = "";
		}

//		td.rc_object_code = 'week_day';
//		td.calendar = this;
		td.week_day_num = wd;
//		RichCalendar.attach_events(td);
	}


var month_days = RichCalendar.get_month_days(this.date);
//	alert(month_days);

// first day of the same month and year as this.date
var date = new Date(this.date);
	date.setDate(1);
var week_day = (date.getDay()+7-this.start_week_day)%7+1;
//	alert(week_day);

// current data
var cur_year = this.date.getFullYear();
var cur_month = this.date.getMonth();
var cur_day = this.date.getDate();
//alert(cur_year + ' => ' + cur_month + ' => ' + cur_day);

// today
var today = new Date();
var today_year = today.getFullYear();
var today_month = today.getMonth();
var today_day = today.getDate();

// 

	var month_names = this.text('monthNames');
	this.head_td.innerHTML = month_names[cur_month] + ', ' + cur_year;


	var row;
	var day;
	var days = 0;
	var last_row;
	for (row=0; row<6; row++) {

		// all days are shown => just check if need to remove unused rows
		if (days == month_days) {
			if (this.cal_tr[last_row+1]) {
				this.cal_tr[last_row+1].parentNode.removeChild(this.cal_tr[last_row+1]);
				this.cal_tr[row] = null;
			}
			continue;
		}

		for (day=0; day<7; day++) {

			if (!this.cal_tr[row]) {
				this.create_cal_row(row);
			}

			var cur_tr = this.cal_tr[row];
			var cell = cur_tr.cells[day];
			cell.className = "";
			// should remove or IE attach the same event several times
			RichCalendar.detach_events(cell);

			if (row==0 && day+1 < week_day || days == month_days) {
				var td_text = '&nbsp;';

//				RichCalendar.detach_events(cell);
			} else {
				var day_num = days+1;
				var td_text = day_num;
				days++;

				cell.rc_object_code = 'day';
				cell.day_num = day_num;
				cell.calendar = this;
				RichCalendar.attach_events(cell);

				// hilight current date
				if (cur_day == day_num) {
					RichCalendar.add_class(cell, "rc_current");
				}

				// hilight today date
				if (day_num == today_day &&
					cur_month == today_month &&
					cur_year == today_year) {
					RichCalendar.add_class(cell, "rc_today");
				}


				var wd = (day+this.start_week_day)%7;

				// hilight weekend days
				if (typeof(weekend_days[wd]) != "undefined") {
					RichCalendar.add_class(cell, "rc_weekend_day");
				} else {
					RichCalendar.remove_class(cell, "rc_weekend_day");
				}

			}
			cell.innerHTML = td_text;

			if (days == month_days) {
				last_row = row;
			}
		}
	}


	// set time
	if (this.show_time && this.hours_obj && this.mins_obj) {
		var hours = this.date.getHours();
		if (hours < 10) hours = '0' + hours;
		var mins = this.date.getMinutes();
		if (mins < 10) mins = '0' + mins;

		this.hours_obj.value = hours;
		this.mins_obj.value = mins;
	}

	// change size of the iframe to fit to its content
/*
	var table_obj = this.iframe_doc.getElementById('rc_iframe_table');
	this.iframe_obj.width = table_obj.offsetWidth;
	this.iframe_obj.height = table_obj.offsetHeight;
*/
	var cal = this;
	window.setTimeout(function(){cal.fit_to_content()}, 1);

	// fix position (need to do this later then calendar is shown as
	// size of calendar could change in this.show(x, y)
	window.setTimeout(function(){cal.fix_position()}, 5);

}

// change size of the iframe to fit to its content
RichCalendar.prototype.fit_to_content = function() {
try {
	var table_obj = this.iframe_doc.getElementById('rc_iframe_table');
	this.iframe_obj.width = '920';
	this.iframe_obj.height = '320';

//alert(this.iframe_obj.width + ' => ' + this.iframe_obj.height);
	// sometimes IE return 0 values, so need to use another approach to
	// determine size of the calendar
	if (!parseInt(this.iframe_obj.width) || !parseInt(this.iframe_obj.height)) {
		this.size_div.innerHTML = this.body_obj.innerHTML;
//alert(this.size_div.offsetWidth + ' => ' + this.size_div.offsetHeight);
	this.iframe_obj.width = this.size_div.offsetWidth;
	this.iframe_obj.height = this.size_div.offsetHeight;
	}

}catch(e){}
}


// create calendar row
RichCalendar.prototype.create_cal_row = function(index) {
var row = this.table_obj.insertRow(3+index);
	row.className = 'rc_cal_tr';

	var cell_indx;
	for (cell_indx=0; cell_indx<7; cell_indx++) {
		var td = row.insertCell(cell_indx);
//		td.innerHTML = index + '-' + cell_indx;
	}

	this.cal_tr[index] = row;

	return row;
}


// changes calendar layout
RichCalendar.prototype.change_skin = function(skin) {
	if (!this.iframe_obj) return;

	var skin_suffix = RichCalendar.skin_suffix(skin);

	this.iframe_obj.className = 'rc_calendar' + skin_suffix;

	this.body_obj.className = 'rc_iframe_body' + skin_suffix;

	this.skin = skin;
}


// returns formatted date (chars recognized are alike used by PHP function date)
RichCalendar.prototype.get_formatted_date = function(format, date) {

	if (!date) date = this.date;
	if (!format) format = this.get_date_format();

	// set time
	if (this.show_time && this.hours_obj && this.mins_obj) {
		this.date.setHours(this.hours_obj.value);
		var mins = this.date.setMinutes(this.mins_obj.value);
	}

var y = date.getFullYear();
var m = date.getMonth();
var d = date.getDate();
var wd = date.getDay();
var hr = date.getHours();
var mins = date.getMinutes();
var secs = date.getSeconds();

var month_names_short = this.text('monthNamesShort');
var month_names = this.text('monthNames');
var day_names_short = this.text('dayNamesShort');
var day_names = this.text('dayNames');

var am = hr < 12 ? true : false;
var hr12 = hr > 12 ? hr - 12 : (hr == 0 ? 12 : hr);

var f = [];

	f["%a"] = am?'am':'pm';
	f["%A"] = am?'AM':'PM';
	f["%d"] = d < 10 ? '0'+d : d; // day of the month, 2 digits with leading zeroes (01 to 31)
	f["%D"] = day_names_short[wd]; // day of the week, textual, short, eg "Fri"
	f["%F"] = month_names[m]; // month, textual, long; eg "January"
	f["%h"] = hr12 < 10 ? '0' + hr12 : hr12; // hour, 12-hour format (01 to 12)
	f["%H"] = hr < 10 ? '0' + hr : hr; // hour, 24-hour format (00 to 23)
	f["%g"] = hr12; // hour, 12-hour format without leading zeros (1 to 12)
	f["%G"] = hr; // hour, 24-hour format without leading zeros (0 to 23)
	f["%i"] = mins < 10 ? '0' + mins : mins; // minutes (00 to 59)
	f["%j"] = d; // day of the month without leading zeros (1 to 31)
	f["%l"] = day_names[wd]; // day of the week, textual, long, eg "Friday"
	f["%L"] = RichCalendar.is_leap_year(y)?1:0; // 1 if leap year, otherwise - 0
	f["%m"] = m < 9 ? '0' + (m+1) : (m+1); //month (01 to 12)
	f["%n"] = m + 1; //month without leading zeros (1 to 12)
	f["%M"] = month_names_short[m]; // month, textual, short, eg "Jan"
	f["%s"] = secs < 10 ? '0' + secs : secs; // seconds (00 to 59)
	f["%t"] = RichCalendar.get_month_days(date); // number of days in the month (28 to 31)
	f["%w"] = wd; // day of the week, numeric (0, Sunday to 6, Saturday)
	f["%Y"] = y; // year, 4 digits, eg 2007
	f["%y"] = String(y).substr(2, 2); // year, 2 digits, eg "07"
	f["%z"] = RichCalendar.get_day_of_year(date); // day of the year (1 to 366)

	var parts = String(format).match(/%./g);
	var i;
	var f_date = format;
	for (i=0; i<parts.length; i++) {
		var value = f[parts[i]];
		if (typeof(value) != "undefined") {
			var re = new RegExp(parts[i], 'g');
			f_date = f_date.replace(re, value);
		}
	}

	return f_date;

}


// set footer content
RichCalendar.prototype.set_footer_text = function(text) {
	if (this.footer_td) {
		this.footer_td.innerHTML = text;
	}
}


// return array with keys - weekend days
RichCalendar.prototype.get_weekend_days = function() {
var weekend_days = this.text('weekend');
var weekend_parts = weekend_days.split(",");
var i;
var result = [];

	for (i=0; i<weekend_parts.length; i++) {
		result[weekend_parts[i]] = true;
	}

	return result;
}


// calendar on close handler; returns true if operation successfull
RichCalendar.prototype.onclose_handler = function() {

	if (this.user_onclose_handler) {
		this.user_onclose_handler(this);
	} else {
		this.hide();
	}

}


// calendar on change handler
RichCalendar.prototype.onchange_handler = function(object_code) {

	if (this.user_onchange_handler) {
		this.user_onchange_handler(this, object_code);
	} else {
		if (object_code == 'day') {

			if (this.value_el) this.value_el.value = this.get_formatted_date();

			if (this.auto_close) this.hide();

		} else {

		}
	}

}


// returns current date format
RichCalendar.prototype.get_date_format = function() {
	var lang_date_format = this.text('dateFormat');
	var format = lang_date_format?lang_date_format:this.format;

	if (this.show_time) {
		format += ' %H:%i';
	}

	return format;
}


// parses date from string str
RichCalendar.prototype.parse_date = function(str, format) {
	if (typeof(str) == "undefined") {
		return;
	}

	if (!format) format = this.get_date_format();


//alert(format);
var today = new Date();
var year = 0;
var month = -1;
var day = 0;
var hours = 0;
var mins = 0;
var seconds = 0;

var month_names = this.text('monthNames');
var short_month_names = this.text('monthNamesShort');

var en_month_names = this.text('monthNames', 'en');
var en_short_month_names = this.text('monthNamesShort', 'en');

//alert(month_names);
	// national chars are not recognized as symbols in regular expressions =>
	// replace them with english month names
	for (j=0; j<month_names.length; j++) {
		var re = new RegExp(month_names[j], 'gi');
		str = str.replace(re, en_month_names[j]);
	}
	for (j=0; j<short_month_names.length; j++) {
		var re = new RegExp(short_month_names[j], 'gi');
		str = str.replace(re, en_short_month_names[j]);
	}

var p = String(str).split(/\W+/g);
var f_p = String(format).match(/%./g);
var i;
var j;
var k;

//alert(p + ' => ' + f_p);
	for (i=0; i<f_p.length; i++) {

		if (!p[i]) continue;

		switch (f_p[i]) {
			case '%a': // am pm
			case '%A':
				if (/am/i.test(p[i]) && hours >= 12) {
					hours -= 12;
				} else {
					if (/pm/i.test(p[i]) && hours < 12) {
						hours += 12;
					}
				}
				break;
			case '%d':
			case '%j':
				day = parseInt(Number(p[i]));
				break;
			case '%F':
				for (j=0; j<en_month_names.length; j++) {
					if (en_month_names[j].toLowerCase() == p[i].toLowerCase()) {
						month = j;
						break;
					}
				}
				break;
			case '%h':
			case '%H':
			case '%g':
			case '%G':
				hours = parseInt(Number(p[i]));
				// to recognize this: 10pm
				if (/am/i.test(p[i]) && hours >= 12) {
					hours -= 12;
				} else {
					if (/pm/i.test(p[i]) && hours < 12) {
						hours += 12;
					}
				}
				break;
			case '%i':
				mins = parseInt(Number(p[i]));
				break;
			case '%m':
			case '%n':
				month = parseInt(Number(p[i]))-1;
				break;
			case '%M':
				for (j=0; j<en_short_month_names.length; j++) {
					if (en_short_month_names[j].toLowerCase() == p[i].toLowerCase()) {
						month = j;
						break;
					}
				}
				break;
			case '%s':
				seconds = parseInt(Number(p[i]));
				break;
			case '%Y':
				year = parseInt(Number(p[i]));
				break;
			case '%y':
				year = parseInt(p[i]);
				if (year < 100) {
					year += year + (year > 29 ? 1900 : 2000);
				}
				break;
			default:
				break;
		}

	}

	if (isNaN(year) || year <= 0) year = today.getFullYear();
	if (isNaN(month) || month < 0 || month > 11) month = today.getMonth();
	if (isNaN(day) || day <= 0 || day > 31) day = today.getDate();
	if (isNaN(hours) || hours < 0 || hours > 23) hours = today.getHours();
	if (isNaN(mins) || mins < 0 || mins > 59) mins = today.getMinutes();
	if (isNaN(seconds) || seconds < 0 || seconds > 59) seconds = today.getSeconds();

//alert(year + ' => ' + month + ' => ' + day + ' => ' + hours + ' => ' + mins + ' => ' + seconds);
	this.date = new Date(year, month, day, hours, mins, seconds);

}